package com.baomidou.mybatisplus.criterion;

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;

/**
 * 查询条件的构造是核心，支持链式拼接<br/>
 * 注意，这个类的目的仅在于方便链式的方法调用，其中真正推荐的方法是and或or，其它方法慎用
 * @author Timothy Ren
 */
public class QueryCriterion4Chain implements QueryCriterion {

	private QueryCriterion currentCriterion;

	/**
	 * 查询条件连接
	 * @param connectMode 连接方式，and或者or
	 * @param qc 待连接的查询条件
	 * @return The QueryCriterion4Chain
	 */
	private QueryCriterion4Chain queryConnect(QueryConnectMode connectMode, QueryCriterion qc) {
		if (currentCriterion == null) {
			currentCriterion = qc;
		} else {
			currentCriterion = new LogicalExpression(connectMode.getConnectStr(), currentCriterion, qc);
		}
		return this;
	}

	/**
	 * 查询条件连接
	 * @param connectMode 连接方式，and或者or
	 * @param criterions 待连接的查询条件
	 * @return The QueryCriterion4Chain
	 */
	private QueryCriterion4Chain queryConnect(QueryConnectMode connectMode, QueryCriterion... criterions) {
		if (currentCriterion == null) {
			currentCriterion = new LogicalExpression(connectMode.getConnectStr(), criterions);
		} else {
			Collection<QueryCriterion> qcs = Collections.singletonList(currentCriterion);
			qcs.addAll(Arrays.asList(criterions));
			currentCriterion = new LogicalExpression(connectMode.getConnectStr(), qcs);
		}
		return this;
	}

	/**
	 * 查询条件and连接
	 * @param qc 待连接的查询条件
	 * @return The QueryCriterion4Chain
	 */
	public QueryCriterion4Chain and(QueryCriterion qc) {
		return queryConnect(QueryConnectMode.AND, qc);
	}

	/**
	 * 多个查询条件and连接
	 * @param criterions 待连接的查询条件
	 * @return The QueryCriterion4Chain
	 */
	public QueryCriterion4Chain and(QueryCriterion... criterions) {
		return queryConnect(QueryConnectMode.AND, criterions);
	}

	/**
	 * 查询条件or连接
	 * @param qc 待连接的查询条件
	 * @return The QueryCriterion4Chain
	 */
	public QueryCriterion4Chain or(QueryCriterion qc) {
		return queryConnect(QueryConnectMode.OR, qc);
	}

	/**
	 * 多个查询条件or连接
	 * @param criterions 待连接的查询条件
	 * @return The QueryCriterion4Chain
	 */
	public QueryCriterion4Chain or(QueryCriterion... criterions) {
		return queryConnect(QueryConnectMode.OR, criterions);
	}

	/**
	 * and拼接一个eq查询条件
	 * @param propertyName 条件属性名
	 * @param value 条件值
	 * @return The QueryCriterion4Chain
	 */
	public QueryCriterion4Chain eq(String propertyName, Object value) {
		return eq(QueryConnectMode.AND, propertyName, value);
	}

	/**
	 * 拼接一个eq查询条件
	 * @param connectMode 条件之间的连接方式
	 * @param propertyName 条件属性名
	 * @param value 条件值
	 * @return The QueryCriterion4Chain
	 */
	public QueryCriterion4Chain eq(QueryConnectMode connectMode, String propertyName, Object value) {
		QueryCriterion qc = new SimpleExpression(propertyName, value, "=");
		return queryConnect(connectMode, qc);
	}

	/**
	 * and拼接eqOrIsNull查询条件
	 * @param propertyName 条件属性名
	 * @param value 条件值
	 * @return The QueryCriterion4Chain
	 */
	public QueryCriterion4Chain eqOrIsNull(String propertyName, Object value) {
		return eqOrIsNull(QueryConnectMode.AND, propertyName, value);
	}

	/**
	 * 拼接一个eqOrIsNull查询条件
	 * @param connectMode 条件之间的连接方式
	 * @param propertyName 条件属性名
	 * @param value 条件值
	 * @return The QueryCriterion4Chain
	 */
	public QueryCriterion4Chain eqOrIsNull(QueryConnectMode connectMode, String propertyName, Object value) {
		QueryCriterion4Chain qc = (value == null ? isNull(connectMode, propertyName)
				: eq(connectMode, propertyName, value));
		return qc;
	}

	/**
	 * and拼接一个ne查询条件
	 * @param propertyName 条件属性名
	 * @param value 条件值
	 * @return The QueryCriterion4Chain
	 */
	public QueryCriterion4Chain ne(String propertyName, Object value) {
		return ne(QueryConnectMode.AND, propertyName, value);
	}

	/**
	 * 拼接一个ne查询条件
	 * @param connectMode 条件之间的连接方式
	 * @param propertyName 条件属性名
	 * @param value 条件值
	 * @return The QueryCriterion4Chain
	 */
	public QueryCriterion4Chain ne(QueryConnectMode connectMode, String propertyName, Object value) {
		QueryCriterion qc = new SimpleExpression(propertyName, value, "<>");
		return queryConnect(connectMode, qc);
	}

	/**
	 * and拼接一个"not equal"或者"not null"查询条件
	 * @param propertyName 条件属性名
	 * @param value 条件值
	 * @return The QueryCriterion4Chain
	 */
	public QueryCriterion4Chain neOrIsNotNull(String propertyName, Object value) {
		return neOrIsNotNull(QueryConnectMode.AND, propertyName, value);
	}

	/**
	 * 拼接一个"not equal"或者"not null"查询条件
	 * @param connectMode 条件之间的连接方式
	 * @param propertyName 条件属性名
	 * @param value 条件值
	 * @return The QueryCriterion4Chain
	 */
	public QueryCriterion4Chain neOrIsNotNull(QueryConnectMode connectMode, String propertyName, Object value) {
		QueryCriterion4Chain qc = (value == null ? isNotNull(connectMode, propertyName)
				: ne(connectMode, propertyName, value));
		return qc;
	}

	/**
	 * and拼接一个like查询条件
	 * @param propertyName 条件属性名
	 * @param value 条件值
	 * @return The QueryCriterion4Chain
	 */
	public QueryCriterion4Chain like(String propertyName, Object value) {
		return like(QueryConnectMode.AND, propertyName, value);
	}

	/**
	 * 拼接一个like查询条件
	 * @param connectMode 条件之间的连接方式
	 * @param propertyName 条件属性名
	 * @param value 条件值
	 * @return The QueryCriterion4Chain
	 */
	public QueryCriterion4Chain like(QueryConnectMode connectMode, String propertyName, Object value) {
		QueryCriterion qc = new SimpleExpression(propertyName, value, " like ");
		return queryConnect(connectMode, qc);
	}

	/**
	 * and拼接一个like查询条件
	 * @param propertyName 条件属性名
	 * @param value 条件值
	 * @param matchMode like匹配格式
	 * @return The QueryCriterion4Chain
	 */
	public QueryCriterion4Chain like(String propertyName, String value, MatchMode matchMode) {
		return like(QueryConnectMode.AND, propertyName, value, matchMode);
	}

	/**
	 * 拼接一个like查询条件
	 * @param connectMode 条件之间的连接方式
	 * @param propertyName 条件属性名
	 * @param value 条件值
	 * @param matchMode like匹配格式
	 * @return The QueryCriterion4Chain
	 */
	public QueryCriterion4Chain like(QueryConnectMode connectMode, String propertyName, String value,
			MatchMode matchMode) {
		QueryCriterion qc = new SimpleExpression(propertyName, matchMode.toMatchString(value), " like ");
		return queryConnect(connectMode, qc);
	}

	/**
	 * and拼接一个"not like"查询条件
	 * @param propertyName 条件属性名
	 * @param value 条件值
	 * @return The QueryCriterion4Chain
	 */
	public QueryCriterion4Chain notLike(String propertyName, Object value) {
		return notLike(QueryConnectMode.AND, propertyName, value);
	}

	/**
	 * 拼接一个"not like"查询条件
	 * @param connectMode 条件之间的连接方式
	 * @param propertyName 条件属性名
	 * @param value 条件值
	 * @return The QueryCriterion4Chain
	 */
	public QueryCriterion4Chain notLike(QueryConnectMode connectMode, String propertyName, Object value) {
		QueryCriterion qc = new SimpleExpression(propertyName, value, " not like ");
		return queryConnect(connectMode, qc);
	}

	/**
	 * Apply a "not like" constraint to the named property
	 * @param propertyName The name of the property
	 * @param value The value to use in comparison
	 * @param matchMode The match mode to use in comparison
	 * @return The SimpleExpression
	 */
	public QueryCriterion4Chain notLike(QueryConnectMode connectMode, String propertyName, String value,
			MatchMode matchMode) {
		QueryCriterion qc = new SimpleExpression(propertyName, matchMode.toMatchString(value), " not like ");
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain notLike(String propertyName, String value, MatchMode matchMode) {
		return notLike(QueryConnectMode.AND, propertyName, value, matchMode);
	}

	/**
	 * Apply a "greater than" constraint to the named property
	 * @param propertyName The name of the property
	 * @param value The value to use in comparison
	 * @return The SimpleExpression
	 */
	public QueryCriterion4Chain gt(QueryConnectMode connectMode, String propertyName, Object value) {
		QueryCriterion qc = new SimpleExpression(propertyName, value, ">");
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain gt(String propertyName, Object value) {
		return gt(QueryConnectMode.AND, propertyName, value);
	}

	/**
	 * Apply a "less than" constraint to the named property
	 * @param propertyName The name of the property
	 * @param value The value to use in comparison
	 * @return The SimpleExpression
	 */
	public QueryCriterion4Chain lt(QueryConnectMode connectMode, String propertyName, Object value) {
		QueryCriterion qc = new SimpleExpression(propertyName, value, "<");
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain lt(String propertyName, Object value) {
		return lt(QueryConnectMode.AND, propertyName, value);
	}

	/**
	 * Apply a "less than or equal" constraint to the named property
	 * @param propertyName The name of the property
	 * @param value The value to use in comparison
	 * @return The SimpleExpression
	 */
	public QueryCriterion4Chain le(QueryConnectMode connectMode, String propertyName, Object value) {
		QueryCriterion qc = new SimpleExpression(propertyName, value, "<=");
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain le(String propertyName, Object value) {
		return le(QueryConnectMode.AND, propertyName, value);
	}

	/**
	 * Apply a "greater than or equal" constraint to the named property
	 * @param propertyName The name of the property
	 * @param value The value to use in comparison
	 * @return The SimpleExpression
	 */
	public QueryCriterion4Chain ge(QueryConnectMode connectMode, String propertyName, Object value) {
		QueryCriterion qc = new SimpleExpression(propertyName, value, ">=");
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain ge(String propertyName, Object value) {
		return ge(QueryConnectMode.AND, propertyName, value);
	}

	/**
	 * Apply a "between" constraint to the named property
	 * @param propertyName The name of the property
	 * @param lo The low value
	 * @param hi The high value
	 * @return The QueryCriterion
	 */
	public QueryCriterion4Chain between(QueryConnectMode connectMode, String propertyName, Object lo, Object hi) {
		QueryCriterion qc = new BetweenExpression(propertyName, lo, hi);
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain between(String propertyName, Object lo, Object hi) {
		return between(QueryConnectMode.AND, propertyName, lo, hi);
	}

	/**
	 * Apply an "in" constraint to the named property
	 * @param propertyName The name of the property
	 * @param values The literal values to use in the IN restriction
	 * @return The QueryCriterion
	 */
	public QueryCriterion4Chain in(QueryConnectMode connectMode, String propertyName, Object... values) {
		QueryCriterion qc = new InExpression(propertyName, values);
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain in(String propertyName, Object... values) {
		return in(QueryConnectMode.AND, propertyName, values);
	}

	/**
	 * Apply an "in" constraint to the named property
	 * @param propertyName The name of the property
	 * @param values The literal values to use in the IN restriction
	 * @return The QueryCriterion
	 */
	public QueryCriterion4Chain in(QueryConnectMode connectMode, String propertyName, Collection values) {
		QueryCriterion qc = new InExpression(propertyName, values.toArray());
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain in(String propertyName, Collection values) {
		return in(QueryConnectMode.AND, propertyName, values);
	}

	/**
	 * Apply an "not in" constraint to the named property
	 * @param propertyName The name of the property
	 * @param values The literal values to use in the IN restriction
	 * @return The QueryCriterion
	 */
	public QueryCriterion4Chain notIn(QueryConnectMode connectMode, String propertyName, Object... values) {
		QueryCriterion qc = new NotInExpression(propertyName, values);
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain notIn(String propertyName, Object... values) {
		return notIn(QueryConnectMode.AND, propertyName, values);
	}

	/**
	 * Apply an "not in" constraint to the named property
	 * @param propertyName The name of the property
	 * @param values The literal values to use in the IN restriction
	 * @return The QueryCriterion
	 */
	public QueryCriterion4Chain notIn(QueryConnectMode connectMode, String propertyName, Collection values) {
		QueryCriterion qc = new NotInExpression(propertyName, values.toArray());
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain notIn(String propertyName, Collection values) {
		return notIn(QueryConnectMode.AND, propertyName, values);
	}

	/**
	 * Apply an "is null" constraint to the named property
	 * @param propertyName The name of the property
	 * @return The QueryCriterion
	 */
	public QueryCriterion4Chain isNull(QueryConnectMode connectMode, String propertyName) {
		QueryCriterion qc = new NullExpression(propertyName);
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain isNull(String propertyName) {
		return isNull(QueryConnectMode.AND, propertyName);
	}

	/**
	 * Apply an "is not null" constraint to the named property
	 * @param propertyName The name of the property
	 * @return The QueryCriterion
	 */
	public QueryCriterion4Chain isNotNull(QueryConnectMode connectMode, String propertyName) {
		QueryCriterion qc = new NotNullExpression(propertyName);
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain isNotNull(String propertyName) {
		return isNotNull(QueryConnectMode.AND, propertyName);
	}

	/**
	 * Apply an "equal" constraint to two properties
	 * @param propertyName One property name
	 * @param otherPropertyName The other property name
	 * @return The PropertyExpression
	 */
	public QueryCriterion4Chain eqProperty(QueryConnectMode connectMode, String propertyName,
			String otherPropertyName) {
		QueryCriterion qc = new PropertyExpression(propertyName, otherPropertyName, "=");
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain eqProperty(String propertyName, String otherPropertyName) {
		return eqProperty(QueryConnectMode.AND, propertyName, otherPropertyName);
	}

	/**
	 * Apply a "not equal" constraint to two properties
	 * @param propertyName One property name
	 * @param otherPropertyName The other property name
	 * @return The PropertyExpression
	 */
	public QueryCriterion4Chain neProperty(QueryConnectMode connectMode, String propertyName,
			String otherPropertyName) {
		QueryCriterion qc = new PropertyExpression(propertyName, otherPropertyName, "<>");
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain neProperty(String propertyName, String otherPropertyName) {
		return neProperty(QueryConnectMode.AND, propertyName, otherPropertyName);
	}

	/**
	 * Apply a "less than" constraint to two properties
	 * @param propertyName One property name
	 * @param otherPropertyName The other property name
	 * @return The PropertyExpression
	 */
	public QueryCriterion4Chain ltProperty(QueryConnectMode connectMode, String propertyName,
			String otherPropertyName) {
		QueryCriterion qc = new PropertyExpression(propertyName, otherPropertyName, "<");
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain ltProperty(String propertyName, String otherPropertyName) {
		return ltProperty(QueryConnectMode.AND, propertyName, otherPropertyName);
	}

	/**
	 * Apply a "less than or equal" constraint to two properties
	 * @param propertyName One property name
	 * @param otherPropertyName The other property name
	 * @return The PropertyExpression
	 */
	public QueryCriterion4Chain leProperty(QueryConnectMode connectMode, String propertyName,
			String otherPropertyName) {
		QueryCriterion qc = new PropertyExpression(propertyName, otherPropertyName, "<=");
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain leProperty(String propertyName, String otherPropertyName) {
		return leProperty(QueryConnectMode.AND, propertyName, otherPropertyName);
	}

	/**
	 * Apply a "greater than" constraint to two properties
	 * @param propertyName One property name
	 * @param otherPropertyName The other property name
	 * @return The PropertyExpression
	 */
	public QueryCriterion4Chain gtProperty(QueryConnectMode connectMode, String propertyName,
			String otherPropertyName) {
		QueryCriterion qc = new PropertyExpression(propertyName, otherPropertyName, ">");
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain gtProperty(String propertyName, String otherPropertyName) {
		return gtProperty(QueryConnectMode.AND, propertyName, otherPropertyName);
	}

	/**
	 * Apply a "greater than or equal" constraint to two properties
	 * @param propertyName One property name
	 * @param otherPropertyName The other property name
	 * @return The PropertyExpression
	 */
	public QueryCriterion4Chain geProperty(QueryConnectMode connectMode, String propertyName,
			String otherPropertyName) {
		QueryCriterion qc = new PropertyExpression(propertyName, otherPropertyName, ">=");
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain geProperty(String propertyName, String otherPropertyName) {
		return geProperty(QueryConnectMode.AND, propertyName, otherPropertyName);
	}

	/**
	 * Return the negation of an expression
	 * @param expression The expression to be negated
	 * @return The NotExpression
	 */
	public QueryCriterion4Chain not(QueryConnectMode connectMode, QueryCriterion expression) {
		QueryCriterion qc = new NotExpression(expression);
		return queryConnect(connectMode, qc);
	}

	public QueryCriterion4Chain not(QueryCriterion expression) {
		return not(QueryConnectMode.AND, expression);
	}

	public String toSqlString() {
		if (currentCriterion == null) {
			return null;
		} else {
			return currentCriterion.toSqlString();
		}
	}

	public Object[] getValueArr() {
		if (currentCriterion == null) {
			return null;
		} else {
			return currentCriterion.getValueArr();
		}
	}

	public String toString() {
		if (currentCriterion == null) {
			return null;
		} else {
			return currentCriterion.toString();
		}
	}
}
